package generator_go

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"gitee.com/woodpile/wgs.ths.code.generate/definition"
	config "gitee.com/woodpile/wgs.ths.config.go"
)

func init() {
	definition.RegGenerator(&Generator{})

	config.DefaultContext.RegisterConfig("generator.plugins[go]", &GeneratorConfig{})
}

const (
	GeneratorName = "go"
)

type GeneratorConfig struct {
	NoFmt bool `yaml:"noFmt,omitempty"`

	Targets []GenerateTarget `yaml:"targets,omitempty"`
}

func (*GeneratorConfig) Name() string {
	return GeneratorName
}

func (*GeneratorConfig) ToDefault(cfg config.IConfigDefault) {
	// ac := cfg.(*GeneratorConfig)
}

type GenerateTarget struct {
	Filepath      string `yaml:"filepath,omitempty"`
	EntryTemplate string `yaml:"entryTemplate,omitempty"`
}

type Generator struct {
	Config *GeneratorConfig
}

func (p *Generator) Name() string {
	return GeneratorName
}

func (p *Generator) Make() (definition.Generator, error) {
	return NewGenerator()
}

func NewGenerator() (*Generator, error) {
	p := &Generator{}

	//配置检查
	cfgI, ok := definition.GlobalConfig.Generator.Plugins[GeneratorName]
	if !ok {
		return nil, fmt.Errorf("generator config not found: %s", GeneratorName)
	}
	cfg, ok := cfgI.(*GeneratorConfig)
	if !ok {
		return nil, fmt.Errorf("generator config type error: %s", GeneratorName)
	}
	p.Config = cfg

	if definition.GlobalConfig.Verbose {
		fmt.Printf("generator config: %+v\n", cfg)
	}

	return p, nil
}

func (p *Generator) Generate(ctx *definition.Context) error {
	for _, target := range p.Config.Targets {
		if err := p.doGenerateTarge(ctx, &target); err != nil {
			return err
		}
	}
	return nil
}

func (p *Generator) doGenerateTarge(ctx *definition.Context, target *GenerateTarget) error {
	template := ctx.Template.RootTemplate.Lookup(target.EntryTemplate)
	if template == nil {
		return fmt.Errorf("template not found: %s", target.EntryTemplate)
	}

	buf := bytes.NewBuffer(nil)
	if err := template.Execute(buf, ctx.DFSet); err != nil {
		return fmt.Errorf("template execute error: %w", err)
	}

	fp := filepath.Join(definition.GlobalConfig.Generator.GenRoot, target.Filepath)
	if err := os.WriteFile(fp, buf.Bytes(), 0o666); err != nil {
		return fmt.Errorf("write target file error: %w", err)
	}

	if !p.Config.NoFmt {
		ec, cancel := context.WithTimeout(context.Background(), 2000*time.Millisecond)
		defer cancel()
		fmtExe := "gofmt" //cspell:disable-line
		if err := exec.CommandContext(ec, "which", fmtExe).Run(); err != nil {
			fmt.Printf("%s not found, skip format: %s\n", fmtExe, err)
		} else {
			if err := exec.CommandContext(ec, fmtExe, "-w", fp).Run(); err != nil {
				fmt.Printf("%s error: %s\n", fmtExe, err)
			}
		}
	}

	return nil
}
